<template>
  <div class="virtual-tree" ref="virtualTree" @scroll="handelScroll">
    <div :style="{ height: `${allHeight}px` }">
      <ul :style="{ transform: `translateY(${scrollTop}px)` }">
        <li
          v-for="item in viewList"
          :key="item.id + guid()"
          :style="{ 'margin-left': `${item.lv * 15}px` }"
          :class="[
            'tree-item',
            item.expand ? 'open' : 'close',
            selectedKeys.includes(item.key) ? 'selected' : '',
          ]"
        >
          <div
            @click="handelExpande(item)"
            :class="['switcher-icon', item.isLeaf ? 'no-child' : '']"
          >
            <slot name="switcherIcon"></slot>
          </div>
          <div class="tree-item-icon" style="padding-left: 8px" v-if="showIcon">
            <slot :name="item.slots.icon"></slot>
          </div>
          <div class="tree-item-name" @click="handelSelectNode(item)">
            {{ item.name }}
          </div>
        </li>
      </ul>
    </div>
  </div>
</template>
<script>
import _ from 'lodash';
export default {
  name: 'RjGroupTree',
  props: {
    treeData: {
      type: Array,
      default: () => [],
    },
    loadData: Function,
    selectedKeys: {
      type: Array,
      default: () => [],
    },
    showIcon: {
      type: Boolean,
      default: false,
    },
    expandedKeys: {
      type: Array,
      default: () => [],
    },
  },
  data() {
    return {
      virtualTreeData: [], //虚拟树扁平化数据
      viewHeight: 450, //显示区域高度高度
      itemHeight: 25, //每行高度,
      showList: [], //所有要显示分组
      viewList: [], //显示的数据
      scrollTop: 0, //滚动高度
      lastTime: 0, //最后更新时间
      timer: null,
      isFirst: true,
    };
  },
  mounted() {
    this.$nextTick(() => {
      const $virtualTree = this.$refs[`virtualTree`];
      console.log(
        'dom',
        $virtualTree.clientHeight,
        $virtualTree.height,
        this,
        this.$el.clientHeight
      );
    });
  },
  watch: {
    treeData: {
      handler(n) {
        this.init(n);
      },
      deep: true,
      immediate: true,
    },
    virtualTreeData: {
      handler(n) {
        //this.getShowList();
        this.getShowList();
        this.getViewList();
        if (this.isFirst) {
          this.scrollToSelectTreeNode();
          this.isFirst = false;
        }
      },
      deep: true,
    },
  },
  computed: {
    // showList() {
    //   return this.virtualTreeData.filter((item) => item.show);
    // },
    allHeight() {
      return this.showList.length * this.itemHeight;
    },
    maxViewSize() {
      return this.viewHeight / this.itemHeight;
    },
  },
  methods: {
    init(treeData) {
      clearTimeout(this.timer);
      this.timer = setTimeout(() => {
        this.virtualTreeData = [];
        this.formatterTree(treeData); //树结构数据扁平化为List
      }, 200);
    },
    async formatterTree(treeData = [], lv = 0) {
      for (let i = 0; i < treeData.length; i++) {
        const item = JSON.parse(JSON.stringify(treeData[i]));
        item.lv = lv;
        item.expand = this.expandedKeys.includes(item.key);
        item.show = true;
        this.virtualTreeData.push(item);
        if (item.children && item.children.length > 0) {
          const nextlv = lv + 1;
          this.formatterTree(item.children, nextlv);
        }
      }
      //treeData.forEach((item) => {

      //});
    },
    handelScroll(e) {
      const timer = +new Date();
      if (timer - this.lastTime > 400) {
        this.scrollTop = e.target.scrollTop;
        this.getViewList();
      }
    },
    getShowList() {
      this.showList = this.virtualTreeData.filter((item) => item.show);
    },
    getViewList() {
      const firstNum = Math.ceil(this.scrollTop / this.itemHeight);
      this.viewList = this.showList.slice(
        firstNum > 0 ? firstNum : 0,
        firstNum + this.maxViewSize
      );
    },
    async handelExpande(group) {
      const index = this.virtualTreeData.findIndex(
        (item) => item.key == group.key
      );
      if (index > -1) {
        this.virtualTreeData[index].expand =
          !this.virtualTreeData[index].expand;
        //判断是否叶子节点，非叶子节点是否已加载数据
        if (
          this.virtualTreeData[index].expand &&
          this.virtualTreeData[index].children == undefined
        ) {
          const nodeData = { dataRef: group };
          //非叶子节点加载数据
          await this.loadData(nodeData);
          //更新子节点数据到非叶子节点
          this.virtualTreeData[index].children = nodeData.dataRef.children;
          //格式化下级节点数据
          const newNode = nodeData.dataRef.children.map((item) => {
            return {
              ...item,
              lv: this.virtualTreeData[index].lv + 1,
              expand: this.expandedKeys.includes(item.key), //在展开路径上的上展开，其他的都关闭
              show: true,
            };
          });
          //将下级节点数据加入虚拟显示列表
          for (let i = newNode.length - 1; i >= 0; i--) {
            this.virtualTreeData.splice(index + 1, 0, newNode[i]);
          }
        }
        let start = index + 1;
        while (
          this.virtualTreeData[start] &&
          this.virtualTreeData[start].lv > this.virtualTreeData[index].lv
        ) {
          if (this.virtualTreeData[index].expand) {
            //如果节点展开
            if (
              this.virtualTreeData[start].pId == this.virtualTreeData[index].id
            ) {
              //如果父节点试当前操作节点
              this.virtualTreeData[start].show = true;
            } else {
              //如果父节点不是当前操作节点
              this.virtualTreeData[start].show = this.getParentExpandStatus(
                this.virtualTreeData[start].pId
              );
            }
          } else {
            //如果节点关闭
            this.virtualTreeData[start].show = false;
          }

          start++;
        }
        this.getShowList();
        this.getViewList();
      }
    },
    //处理选中滚动
    scrollToSelectTreeNode() {
      if (this.selectedKeys.length > 0) {
        const groupId = this.selectedKeys[0];
        const index = this.showList.findIndex((item) => item.key == groupId);
        this.$nextTick(() => {
          //当DOM是显示的的时候
          if (this.$refs.virtualTree.clientHeight > 0) {
            this.scrollTop =
              this.itemHeight * (index + 1) > this.viewHeight
                ? this.itemHeight * (index + 1) - this.viewHeight
                : 0;
            this.$refs.virtualTree.scrollTo(0, this.scrollTop);
          }
        });
      }
    },
    //选中状态
    handelSelectNode(node) {
      if (node.selectable) {
        this.$emit('select', [node.key]);
        const expendPath = this.getExpandPath(node.pId);
        this.$emit('expand', expendPath);
      }
    },
    //获取父节点打开关闭状态
    getParentExpandStatus(nodeId) {
      const node = this.virtualTreeData.find((item) => item.id == nodeId);
      return node.expand;
    },
    getExpandPath(pId) {
      const path = [pId];
      let testId = pId;
      //debugger;
      while (testId != 0) {
        const res = this.virtualTreeData.find((item) => item.key == testId);
        if (res) {
          path.push(res.pId);
          testId = res.pId;
        } else {
          testId = 0;
        }
      }
      return path;
    },
    guid() {
      return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(
        /[xy]/g,
        function (c) {
          var r = (Math.random() * 16) | 0,
            v = c == 'x' ? r : (r & 0x3) | 0x8;
          return v.toString(16);
        }
      );
    },
  },
};
</script>
<style lang="less" scoped>
.virtual-tree {
  height: 100%;
  line-height: 25px;
  .tree-item {
    display: flex;
    align-items: center;
    .tree-item-icon,
    .tree-item-name {
      cursor: pointer;
    }
    .tree-item-name {
      padding: 0 8px;
    }
    .switcher-icon {
      cursor: pointer;
      transition: transform 0.3s;
    }
    &.open {
      .switcher-icon {
        transform: rotate(0deg);
      }
    }
    &.close {
      .switcher-icon {
        transform: rotate(-90deg);
      }
    }
    &.selected {
      .tree-item-icon,
      .tree-item-name {
        background-color: #cfe4ff;
      }
    }
  }
}
.no-child {
  visibility: hidden;
}
</style>
